]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
NetworkManager: show portal logins when required
authorGiovanni Campagna <gcampagna@src.gnome.org>
Mon, 17 Feb 2014 17:06:35 +0000 (18:06 +0100)
committerGiovanni Campagna <gcampagna@src.gnome.org>
Thu, 26 Jun 2014 17:55:02 +0000 (19:55 +0200)
Listen to changes in connectivity, and ask our helper to authenticate
when needed.
We don't have a URL to connect to yet (we will have when
the new NM API lands), so we use the default of trying
www.gnome.org (which is also more reliable because we can
recognize when the login is done)

https://bugzilla.gnome.org/show_bug.cgi?id=704416

js/ui/status/network.js

index f3e568dd644ae6e2fccf7b7fac0769d5a71a256e..049406d08f00b24d3ee040c06278ea7e98a2bb2d 100644 (file)
@@ -44,6 +44,33 @@ const NM80211Mode = NetworkManager['80211Mode'];
 const NM80211ApFlags = NetworkManager['80211ApFlags'];
 const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
 
+const PortalHelperResult = {
+    CANCELLED: 0,
+    COMPLETED: 1,
+    RECHECK: 2
+};
+
+const PortalHelperIface = '<node> \
+<interface name="org.gnome.Shell.PortalHelper"> \
+<method name="Authenticate"> \
+    <arg type="o" direction="in" name="connection" /> \
+    <arg type="s" direction="in" name="url" /> \
+    <arg type="u" direction="in" name="timestamp" /> \
+</method> \
+<method name="Close"> \
+    <arg type="o" direction="in" name="connection" /> \
+</method> \
+<method name="Refresh"> \
+    <arg type="o" direction="in" name="connection" /> \
+</method> \
+<signal name="Done"> \
+    <arg type="o" name="connection" /> \
+    <arg type="u" name="result" /> \
+</signal> \
+</interface> \
+</node>';
+const PortalHelperProxy = Gio.DBusProxy.makeProxyWrapper(PortalHelperIface);
+
 function ssidCompare(one, two) {
     if (!one || !two)
         return false;
@@ -1581,6 +1608,7 @@ const NMApplet = new Lang.Class({
 
         this._activeConnections = [ ];
         this._connections = [ ];
+        this._connectivityQueue = [ ];
 
         this._mainConnection = null;
         this._mainConnectionIconChangedId = 0;
@@ -1609,6 +1637,7 @@ const NMApplet = new Lang.Class({
         this._client.connect('notify::primary-connection', Lang.bind(this, this._syncMainConnection));
         this._client.connect('notify::activating-connection', Lang.bind(this, this._syncMainConnection));
         this._client.connect('notify::active-connections', Lang.bind(this, this._syncVPNConnections));
+        this._client.connect('notify::connectivity', Lang.bind(this, this._syncConnectivity));
         this._client.connect('device-added', Lang.bind(this, this._deviceAdded));
         this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved));
         this._settings.connect('new-connection', Lang.bind(this, this._newConnection));
@@ -1777,6 +1806,7 @@ const NMApplet = new Lang.Class({
         }
 
         this._updateIcon();
+        this._syncConnectivity();
     },
 
     _syncVPNConnections: function() {
@@ -1882,6 +1912,97 @@ const NMApplet = new Lang.Class({
     _syncNMState: function() {
         this.indicators.visible = this._client.manager_running;
         this.menu.actor.visible = this._client.networking_enabled;
+
+        this._syncConnectivity();
+    },
+
+    _flushConnectivityQueue: function() {
+        if (this._portalHelperProxy) {
+            for (let item of this._connectivityQueue)
+                this._portalHelperProxy.CloseRemote(item);
+        }
+
+        this._connectivityQueue = [];
+    },
+
+    _closeConnectivityCheck: function(path) {
+        let index = this._connectivityQueue.indexOf(path);
+
+        if (index >= 0) {
+            if (this._portalHelperProxy)
+                this._portalHelperProxy.CloseRemote(path);
+
+            this._connectivityQueue.splice(index, 1);
+        }
+    },
+
+    _portalHelperDone: function(proxy, emitter, parameters) {
+        let [path, result] = parameters;
+
+        if (result == PortalHelperResult.CANCELLED) {
+            // Keep the connection in the queue, so the user is not
+            // spammed with more logins until we next flush the queue,
+            // which will happen once he chooses a better connection
+            // or we get to full connectivity through other means
+        } else if (result == PortalHelperResult.COMPLETED) {
+            this._closeConnectivityCheck(path);
+            return;
+        } else if (result == PortalHelperResult.RECHECK) {
+            this._client.check_connectivity_async(null, Lang.bind(this, function(client, result) {
+                try {
+                    let state = client.check_connectivity_finish(result);
+                    if (state >= NetworkManager.ConnectivityState.FULL)
+                        this._closeConnectivityCheck(path);
+                } catch(e) { }
+            }));
+        } else {
+            log('Invalid result from portal helper: ' + result);
+        }
+    },
+
+    _syncConnectivity: function() {
+        if (this._mainConnection == null ||
+            this._mainConnection.state != NetworkManager.ActiveConnectionState.ACTIVATED) {
+            this._flushConnectivityQueue();
+            return;
+        }
+
+        let isPortal = this._client.connectivity == NetworkManager.ConnectivityState.PORTAL;
+        // For testing, allow interpreting any value != FULL as PORTAL, because
+        // LIMITED (no upstream route after the default gateway) is easy to obtain
+        // with a tethered phone
+        // NONE is also possible, with a connection configured to force no default route
+        // (but in general we should only prompt a portal if we know there is a portal)
+        if (GLib.getenv('GNOME_SHELL_CONNECTIVITY_TEST') != null)
+            isPortal = isPortal || this._client.connectivity < NetworkManager.ConnectivityState.FULL;
+        if (!isPortal)
+            return;
+
+        let path = this._mainConnection.get_path();
+        for (let item of this._connectivityQueue) {
+            if (item == path)
+                return;
+        }
+
+        let timestamp = global.get_current_time();
+        if (this._portalHelperProxy) {
+            this._portalHelperProxy.AuthenticateRemote(path, '', timestamp);
+        } else {
+            new PortalHelperProxy(Gio.DBus.session, 'org.gnome.Shell.PortalHelper',
+                                  '/org/gnome/Shell/PortalHelper', Lang.bind(this, function (proxy, error) {
+                                      if (error) {
+                                          log('Error launching the portal helper: ' + error);
+                                          return;
+                                      }
+
+                                      this._portalHelperProxy = proxy;
+                                      proxy.connectSignal('Done', Lang.bind(this, this._portalHelperDone));
+
+                                      proxy.AuthenticateRemote(path, '', timestamp);
+                                  }));
+        }
+
+        this._connectivityQueue.push(path);
     },
 
     _updateIcon: function() {